Skip to content

lnwallet+walletrpc: add WalletKit.SubmitPackage for v3 CPFP package relay#10900

Merged
yyforyongyu merged 4 commits into
lightningnetwork:masterfrom
ellemouton:walletkit-submitpackage
Jun 30, 2026
Merged

lnwallet+walletrpc: add WalletKit.SubmitPackage for v3 CPFP package relay#10900
yyforyongyu merged 4 commits into
lightningnetwork:masterfrom
ellemouton:walletkit-submitpackage

Conversation

@ellemouton

@ellemouton ellemouton commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds a WalletKit.SubmitPackage RPC, letting a client submit a package of
related transactions (topologically sorted, unconfirmed parents first and the
child last) to lnd's chain backend for atomic validation and acceptance.

This is what allows a v3/TRUC package to relay: a zero-fee parent carrying
an ephemeral (P2A) anchor is below the minimum relay fee and is rejected by a
standalone broadcast, but is accepted when submitted together with a
fee-paying CPFP child whose combined feerate clears policy. TestMempoolAccept
already exposes Core's testmempoolaccept this way; SubmitPackage is the
broadcasting counterpart for submitpackage.

Details

  • New SubmitPackage method on the lnwallet.WalletController interface.
    BtcWallet forwards it to the chain backend. Only the bitcoind backend
    performs real package submission (via the node's submitpackage RPC); the
    btcd backend has no submitpackage handler and returns
    ErrUnimplemented; neutrino (no mempool) broadcasts each transaction
    over P2P best-effort, relying on the peer's 1p1c package relay, and returns
    a synthetic result.
  • The WalletKit.SubmitPackage handler decodes the raw transactions (capped at
    maxPackageTxns = 25, mirroring bitcoind's MAX_PACKAGE_COUNT), forwards an
    optional sat_per_vbyte ceiling (unset = node default; 0 = no limit, which
    a high-feerate CPFP child needs), and maps the node result back to the proto
    response. Gated by the onchain:write macaroon permission.
  • A wallet submitpackage lncli command.
  • Mock wallet controllers and the no-chain backend gain implementations
    (NoChainSource returns errNotImplemented).

Dependencies

Builds on the btcd-v2-modules lnd master and the released btcwallet
v0.18.0
, which carries the chain.Interface.SubmitPackage method
(btcsuite/btcwallet#1264). No replace directives.

Testing

make rpc reproduces the generated stubs cleanly and cmd/lnd builds. Adds an
integration test (testSubmitPackage) that submits a zero-fee v3 parent plus a
fee-paying CPFP child and asserts the package is accepted, lands in the
mempool, and confirms — exercising the CPFP path end to end. A zero-fee parent
only relays to a package-relay-capable node, so the test runs on the bitcoind
backend with a bitcoind miner (minerbackend=bitcoind) and skips otherwise.
Also adds a unit test pinning the sat/vByte -> BTC/kvB fee-rate conversion.

@github-actions github-actions Bot added the severity-critical Requires expert review - security/consensus critical label Jun 11, 2026
@github-actions

Copy link
Copy Markdown

PR Severity: CRITICAL. Files in lnwallet/* (lnwallet/interface.go, lnwallet/btcwallet/btcwallet.go, lnwallet/mock.go) drive the CRITICAL classification. Also touches lnrpc/* (HIGH) and proto/other files (MEDIUM). 10 non-excluded files, ~250 non-excluded lines - no bump needed. <!-- pr-severity-bot -->

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a new SubmitPackage RPC method to the WalletKit service, allowing clients to submit topologically sorted transaction packages to the chain backend. This capability is essential for supporting v3/TRUC package relay, which allows zero-fee parent transactions to be accepted when paired with a fee-paying CPFP child. The changes include updates to the wallet interface, backend implementations, and necessary gRPC/protobuf infrastructure.

Highlights

  • New RPC Method: Added the WalletKit.SubmitPackage RPC method to enable atomic submission of transaction packages for v3/TRUC relay support.
  • Interface Updates: Updated the WalletController interface to include SubmitPackage, ensuring consistency across wallet implementations.
  • Implementation: Implemented SubmitPackage logic in BtcWallet and provided mock implementations for testing and development environments.
  • API Definitions: Updated protobuf, gRPC, and Swagger definitions to expose the new SubmitPackage functionality to external clients.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new SubmitPackage RPC endpoint to the WalletKit service, enabling the submission of a package of related transactions for atomic validation and acceptance. This feature is implemented across the wallet controller interface, mock implementations, and the btcwallet backend. The review feedback suggests several important improvements: adding a defensive nil check on the backend's returned result in walletkit_server.go to prevent a potential panic, documenting the stub implementation in no_chain_backend.go to comply with the repository style guide, and marking the max_fee_rate field as optional in the protobuf definition to safely distinguish between an omitted field and an explicit zero limit.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread lnrpc/walletrpc/walletkit_server.go Outdated
Comment thread chainreg/no_chain_backend.go
Comment thread lnrpc/walletrpc/walletkit.proto Outdated
@ellemouton ellemouton force-pushed the walletkit-submitpackage branch from 65de675 to d290c19 Compare June 11, 2026 20:08
@ellemouton

Copy link
Copy Markdown
Collaborator Author

Thanks for the review — addressed all three:

  • nil result check: the handler now returns an error if the wallet backend returns a nil result, before mapping its fields.
  • NoChainSource.SubmitPackage doc: added a godoc comment beginning with the function name.
  • max_fee_rate: changed to optional double so an explicit 0 (no limit) is distinguishable from an omitted value (node default); the handler passes the *float64 through directly.

Also fixed up the CI failures: added the missing REST annotation for the new RPC (POST /v2/wallet/tx/package) and a release-notes entry, plus added an lncli wallet submitpackage command and an integration test.

func testSubmitPackage(ht *lntest.HarnessTest) {
// submitpackage is a bitcoind RPC: btcd has no equivalent and neutrino
// has no mempool, so this test only applies to the bitcoind backend.
if ht.ChainBackendName() != "bitcoind" {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we also run it with neutrino? as the implementation would suggest it works there

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point — it does work on neutrino, but only against a package-aware miner. neutrino has no mempool, so SubmitPackage just broadcasts parent+child and leans on the peer's 1p1c relay — that needs the bitcoind miner (minerbackend=bitcoind, v28+); against the default btcd miner the zero-fee parent is simply rejected and never assembles. so a neutrino row would have to be gated on minerbackend=bitcoind and assert confirmation (the neutrino result itself is a synthetic best-effort one). afaict there's no backend=neutrino+minerbackend=bitcoind combo in CI today, so it'd be skipped everywhere unless we add that job. happy to add the gated test (+ the CI combo) if you think it's worth it — wdyt?

@ellemouton ellemouton force-pushed the walletkit-submitpackage branch 2 times, most recently from 2428104 to bfabceb Compare June 15, 2026 13:14
@github-actions github-actions Bot added severity-critical Requires expert review - security/consensus critical and removed severity-critical Requires expert review - security/consensus critical labels Jun 15, 2026
@ellemouton ellemouton force-pushed the walletkit-submitpackage branch 2 times, most recently from a928b90 to 3983397 Compare June 15, 2026 17:39
Comment thread lnrpc/walletrpc/walletkit.proto Outdated
Comment thread lnwallet/interface.go Outdated
Comment thread itest/lnd_submit_package_test.go
@ellemouton ellemouton force-pushed the walletkit-submitpackage branch 6 times, most recently from 7a5ce08 to ceaea5f Compare June 18, 2026 15:25
@ellemouton ellemouton force-pushed the walletkit-submitpackage branch from ceaea5f to dd84372 Compare June 25, 2026 05:21
@ellemouton ellemouton marked this pull request as ready for review June 25, 2026 14:04
@ellemouton ellemouton force-pushed the walletkit-submitpackage branch from dd84372 to b65a966 Compare June 25, 2026 14:13
@github-actions github-actions Bot added severity-critical Requires expert review - security/consensus critical and removed severity-critical Requires expert review - security/consensus critical labels Jun 25, 2026
@ellemouton ellemouton force-pushed the walletkit-submitpackage branch from b65a966 to 72c7096 Compare June 25, 2026 15:06
@github-actions github-actions Bot added the severity-critical Requires expert review - security/consensus critical label Jun 25, 2026
@ellemouton ellemouton requested a review from Roasbeef June 26, 2026 15:27
@yyforyongyu

Copy link
Copy Markdown
Member

/gateway review

@lightninglabs-gateway

lightninglabs-gateway Bot commented Jun 29, 2026

Copy link
Copy Markdown

✅ Review posted: #10900 (review)

10 finding(s); 10 inline, 0 in body.

🔁 Need a re-review after pushing changes? Reply with /gateway re-review.
Maintainers can also /gateway dismiss <id> to silence specific findings, or anyone can /gateway explain <id> for elaboration.

@lightninglabs-gateway lightninglabs-gateway Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds a WalletKit.SubmitPackage RPC (plus a WalletController.SubmitPackage interface method, BtcWallet/mock implementations, an lncli command, and an integration test) so a client can submit a topologically-sorted v3/TRUC package — a zero-fee parent plus a fee-paying CPFP child — to the chain backend for atomic acceptance. The intent is sound, the end-to-end itest is a genuine proof of the CPFP path, and the fee-rate unit conversion (sat/vByte → BTC/kvB) is dimensionally and numerically correct.

The concerns concentrate in three areas. First, the fee-safety knob is a footgun: an explicit sat_per_vbyte = 0 is overloaded to mean "disable the ceiling entirely," and both the docs and the example/itest nudge users toward it — removing overpay protection on their own funds. Second, the neutrino path cannot truly satisfy package relay (it broadcasts each tx independently, ignores the fee ceiling, isn't atomic, and can return an unverifiable "success"). Third, the public surface has several correctness/contract sharp edges: untyped success signaling, an unbounded input, and a wtxid-vs-txid result shape. A breaking addition to an exported interface also ships without a release-notes entry.

Findings: 🔴 0 Blocker · 🟠 5 Major · 🟡 5 Minor · 🔵 0 Nit

Comment thread lnrpc/walletrpc/walletkit.proto
// and relies on the connected full node's 1p1c package relay; a synthetic
// success result is returned since a light client cannot report per-tx package
// acceptance.
func (b *BtcWallet) SubmitPackage(txns []*wire.MsgTx,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 Major F2: Neutrino SubmitPackage is not atomic and can report unverifiable success

The neutrino branch loops PublishTransaction(tx, "") over txns individually, ignores maxFeeRate entirely (no fee ceiling is ever applied on neutrino), and on success synthesizes PackageMsg: "success". This breaks the package-atomicity contract that the whole feature depends on: a zero-fee v3 parent broadcast standalone is invalid, so either the call errors at the parent (the feature does not work on neutrino) or — if an earlier tx publishes and a later one fails — the mempool is left with a partial, non-atomic subset with no rollback. When every tx happens to publish, the synthesized "success" asserts a package-accept verdict a light client cannot substantiate, so downstream logic (sweeps, force-close bumps) may proceed on a false premise. On failure the loop returns a bare err with no index/txid, so the partial-broadcast state is undiagnosable. The neutrino path should either return an explicit "unsupported/unverified" result or, at minimum, not claim "success" and should wrap publish errors with the offending tx.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(F2) Right — neutrino has no mempool, so it can't substantiate a package-accept verdict. It does a best-effort individual P2P broadcast (relying on the peer's 1p1c relay) and returns a synthetic result; only bitcoind performs real submitpackage. The proto + method docs now call this out explicitly as best-effort/non-atomic. Happy to return a distinct non-"success" package_msg for the neutrino path if you'd prefer a clearer signal.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed: the neutrino branch is removed — BtcWallet.SubmitPackage now returns ErrUnimplemented for both btcd and neutrino (only bitcoind does real submitpackage). It no longer fabricates a "success" verdict a light client can't substantiate, and there's no partial-broadcast path (the old loop would have failed at the zero-fee parent anyway, since it's below min-relay-fee on its own). Callers that want neutrino relay broadcast the txns individually and lean on the peer's 1p1c relay. Proto + method docs updated to match.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update (after discussion with @yyforyongyu, went with the best-effort option rather than erroring): the neutrino path still broadcasts each tx individually and relies on the peer's 1p1c relay, but it no longer claims "success" — it returns a deliberately non-success PackageMsg ("neutrino best-effort broadcast; acceptance unverified") and wraps publish errors with the offending tx index + txid, so callers treat it as an unverified broadcast, not a package-accept verdict.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shortened the neutrino PackageMsg to "broadcast-unverified" (the rationale is in the const's doc comment) — less verbose than the first pass.

Comment thread lnrpc/walletrpc/walletkit_server.go Outdated

// packageSubmitter is the subset of the wallet controller that can submit a
// package of transactions for atomic validation/acceptance. The
// btcwallet-backed WalletController implements it; other controllers do not.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 Major F3: No bound on package size; unbounded raw_txs decode

The handler accepts req.RawTxs with no cap on count or per-tx size, pre-allocating make([]*wire.MsgTx, 0, len(req.RawTxs)) and calling tx.Deserialize on every element before any validity check. wire.MsgTx.Deserialize honors length prefixes in the byte stream and allocates per-input/output slices accordingly, so a small request body can drive large allocations (bandwidth amplification). While onchain:write gates this, that macaroon can be delegated/RBAC-scoped, so an authenticated-but-not-operator caller can force outsized work. A real package is at most a parent+child (bitcoind caps packages at 25); reject when len(req.RawTxs) exceeds a small bound and cap individual raw-tx size before deserializing. Documenting the cap in the proto/lncli would also make the limit part of the published contract.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done (F3) — the handler now rejects packages larger than maxPackageTxns = 25 (mirroring bitcoind's MAX_PACKAGE_COUNT) before deserializing, bounding the work an authenticated caller can force.

// limit) is passed through unchanged.
var maxFeeRate *chainfee.SatPerVByte
if req.SatPerVbyte != nil {
rate := chainfee.SatPerVByte(*req.SatPerVbyte)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 Major F4: Package rejection isn't surfaced as an error; success is a free-form string

Whole-package acceptance is conveyed only through PackageMsg, a free-form string the caller must compare against the literal "success" (the itest does require.Equal(ht, "success", ...)). The handler returns a nil gRPC error whenever the backend call returns no Go error, even if bitcoind rejected the package (PackageMsg != "success") or individual txns failed (tx_results[*].error populated) — so a caller that checks only the gRPC status believes an on-chain funds operation succeeded when it did not. Per-tx errors are likewise opaque strings, so a caller cannot machine-distinguish "fee too low" from "missing inputs" from "already in mempool" to decide its next action. Add a typed status (e.g. an enum: fully-accepted / partially-accepted / rejected) and reserve package_msg for human-readable detail; a brittle string contract on a public RPC is hard to change later.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(F4) Per-tx failures are surfaced via tx_results[*].error and package_msg carries bitcoind's verdict, mirroring the submitpackage RPC's own result shape, so a fully-rejected package returns the detail in the response rather than a gRPC error. If we want callers to machine-distinguish accepted/partial/rejected without parsing package_msg, I can add a typed status enum (or return a gRPC error on non-acceptance) — lmk which you'd prefer.

Comment thread lnwallet/interface.go
Comment thread lnrpc/walletrpc/walletkit_server.go
Comment thread lnrpc/walletrpc/walletkit_server.go
Comment thread chainreg/no_chain_backend.go
Comment thread lnwallet/btcwallet/btcwallet.go Outdated
// 1 sat/vByte = 1000 sat/kvB, and SatoshiPerBitcoin sats make a
// BTC, so BTC/kvB = sat/vByte * 1000 / SatoshiPerBitcoin.
btcPerKvB := float64(*maxFeeRate) * 1000 /
btcutil.SatoshiPerBitcoin

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Minor F9: Fee-rate conversion is correct but untested, with magic constant and integer-only granularity

btcPerKvB := float64(*maxFeeRate) * 1000 / btcutil.SatoshiPerBitcoin is dimensionally and numerically correct (1 sat/vByte → 0.00001 BTC/kvB; bitcoind's 0.10 BTC/kvB default = 10 sat/vByte), but: there is no unit test pinning a known input, so an off-by-1000 regression here would silently relax or tighten the user's fee ceiling; the 1000 (vByte→kvB) is an inline magic number; and because chainfee.SatPerVByte is integer, only whole sat/vByte ceilings are expressible and very large values lose integer precision once past 2^53 in the float64 product. Add a conversion unit test, name the constant, and document the integer-granularity limitation on the parameter.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done (F9) — extracted satPerVByteToBTCPerKvB with a named vBytesPerKvB constant (documenting the integer-granularity limitation) and added a unit test pinning the known reference points.

Comment thread go.mod
@lightninglabs-gateway

Copy link
Copy Markdown

🤖 gateway audit metadata for this PR — auto-generated, please don't edit.

@yyforyongyu yyforyongyu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool some valid issues found by agents, think we should address them first.

Comment thread lnrpc/walletrpc/walletkit.proto Outdated
/* lncli: `wallet submitpackage`
SubmitPackage submits a package of related transactions (topologically
sorted, unconfirmed parents first and the child last) for atomic
validation and acceptance. For bitcoind/btcd backends it uses the node's

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is inaccurate for two backends. The btcd backend in btcwallet v0.18.0 returns ErrUnimplemented for SubmitPackage, so it does not use submitpackage. Neutrino also does not provide atomic validation/acceptance; it can only attempt non-atomic individual P2P broadcasts. The RPC docs should probably say bitcoind-only for real package submission, and clearly mark neutrino as best-effort/non-atomic if it remains supported.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — reworded so it's clear only the bitcoind backend performs real package submission (via submitpackage); the btcd backend returns an error, and neutrino is best-effort/non-atomic (individual P2P broadcast relying on 1p1c relay).

Comment thread lnwallet/btcwallet/btcwallet.go Outdated
// SubmitPackage submits a package of related transactions (topologically
// sorted, parents first and child last) for atomic validation and acceptance.
//
// For bitcoind/btcd backends it uses the node's submitpackage RPC, which lets

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same backend-support issue here: this says bitcoind/btcd, but the btcwallet v0.18.0 btcd backend returns ErrUnimplemented for SubmitPackage. This should be narrowed to bitcoind unless btcd support lands.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — narrowed the doc: only bitcoind does real submission; the btcd backend has no submitpackage handler and returns ErrUnimplemented.

Comment thread lnrpc/walletrpc/walletkit_server.go Outdated
txns = append(txns, tx)
}

submitter, ok := w.cfg.Wallet.(packageSubmitter)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This type assertion is always true now that SubmitPackage was added to the lnwallet.WalletController interface and w.cfg.Wallet is statically a WalletController. Either keep SubmitPackage off the base interface and make this an optional capability, or remove the local interface/assertion and call w.cfg.Wallet.SubmitPackage directly.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — SubmitPackage is on WalletController now, so the assertion was always true. Removed the packageSubmitter interface + assertion and call w.cfg.Wallet.SubmitPackage directly (also dropped the now-unused btcjson import).

Comment thread .golangci.yml Outdated
- google.golang.org/protobuf
- github.com/lightningnetwork/lnd/sqldb
- github.com/lightningnetwork/lightning-onion
- github.com/btcsuite/btcwallet

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These replace-allow-list entries look stale now that go.mod no longer has a replace for btcwallet/btcd. Since the preceding comment points readers to go.mod for the reason, please remove these draft-era allow-list entries unless a replace is still expected before merge.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — removed the stale btcwallet/btcd replace-allow-list entries; go.mod no longer replaces them.

Comment thread chainreg/no_chain_backend.go
Comment thread lnrpc/walletrpc/walletkit.proto
Comment thread lnwallet/interface.go
Comment thread lnrpc/walletrpc/walletkit_server.go
@ellemouton ellemouton force-pushed the walletkit-submitpackage branch 2 times, most recently from 455bbe7 to ac88f0a Compare June 29, 2026 18:56
@github-actions github-actions Bot added severity-critical Requires expert review - security/consensus critical and removed severity-critical Requires expert review - security/consensus critical labels Jun 29, 2026
@ellemouton ellemouton force-pushed the walletkit-submitpackage branch from ac88f0a to c5c3723 Compare June 29, 2026 21:54
@ellemouton

Copy link
Copy Markdown
Collaborator Author

Thanks for the thorough review @yyforyongyu (and the gateway agent). Pushed fixes for all the actionable findings — summary:

Addressed (code):

  • F3 — handler now caps packages at maxPackageTxns = 25 (mirrors bitcoind's MAX_PACKAGE_COUNT) before deserializing.
  • F6 — dropped the now-redundant packageSubmitter interface + assertion; call w.cfg.Wallet.SubmitPackage directly.
  • F8NoChainSource.SubmitPackage returns errNotImplemented.
  • F9 — extracted satPerVByteToBTCPerKvB (named vBytesPerKvB const, documented integer granularity) + added a conversion unit test.
  • F10 — PR description updated to the released-version reality (btcwallet v0.18.0, no replace).
  • Proto + btcwallet.go docs reworded: only bitcoind does real package submission; btcd returns ErrUnimplemented; neutrino is best-effort/non-atomic.
  • .golangci.yml — removed the stale btcwallet/btcd replace-allow-list entries.

Dismissed per your /gateway notes: F1 (sat_per_vbyte=0 documented, aligns with bitcoind), F5 (no downstream consumers), F7 (btcjson decoder requires a parseable txid).

Left as-is unless you'd prefer otherwise (design calls):

  • F2 — neutrino can't substantiate a package-accept verdict (no mempool); kept the documented best-effort 1p1c broadcast + synthetic result. Happy to return a distinct non-"success" package_msg if preferred.
  • F4 — per-tx errors are surfaced in tx_results[*].error and package_msg carries bitcoind's verdict; can add a typed status enum / return a gRPC error on non-acceptance if we want that to be machine-distinguishable.

CI is green modulo known flakes (neutrino reorg, htlc-multihop, a docker-pull timeout). PTAL 🙏

@ellemouton ellemouton requested a review from yyforyongyu June 30, 2026 16:54
Comment thread lnwallet/btcwallet/btcwallet.go Outdated
}

return &btcjson.SubmitPackageResult{
PackageMsg: "success",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

think we should address https://github.com/lightningnetwork/lnd/pull/10900/changes#r3493050462 here - maybe name it published or broadcast-unverified? just to make it clear at the callsite so it can check

@ellemouton ellemouton force-pushed the walletkit-submitpackage branch from c5c3723 to c432322 Compare June 30, 2026 17:17

@bhandras bhandras left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, nice work!

Comment thread lnrpc/walletrpc/walletkit.proto
Comment thread itest/lnd_submit_package_test.go Outdated
@ellemouton ellemouton force-pushed the walletkit-submitpackage branch from c432322 to ef55a11 Compare June 30, 2026 17:37
@github-actions github-actions Bot added severity-critical Requires expert review - security/consensus critical and removed severity-critical Requires expert review - security/consensus critical labels Jun 30, 2026

@yyforyongyu yyforyongyu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pending CIs, otherwise LGTM!

Comment thread lnwallet/btcwallet/btcwallet.go
@ellemouton ellemouton force-pushed the walletkit-submitpackage branch from ef55a11 to 4aae817 Compare June 30, 2026 18:03
Add SubmitPackage to the lnwallet.WalletController interface and a new
WalletKit.SubmitPackage RPC, so a client of lnd can relay a package of
related transactions (parents first, child last) through lnd's own chain
connection. This lets a zero-fee v3/TRUC parent be accepted via its
fee-paying CPFP child without the caller needing a separate connection to
the chain backend.

BtcWallet.SubmitPackage forwards to the chain backend's submitpackage for
bitcoind/btcd, and broadcasts each transaction individually for neutrino
(no mempool; relies on the peer's 1p1c package relay). The WalletKit
handler maps the proto request/response to the btcjson result and is
gated by the onchain:write macaroon permission. Mock controllers and the
no-chain backend gain trivial implementations.
Add a `wallet submitpackage` command that takes one or more hex-encoded
raw transactions (topologically sorted, parents first and the child
last) and an optional --max_fee_rate, and submits them as a package via
the WalletKit.SubmitPackage RPC.
Add an integration test that exercises WalletKit.SubmitPackage: it builds
a zero-fee v3 (TRUC) parent that a standalone broadcast would reject,
pairs it with a fee-paying v3 CPFP child, and asserts the package is
accepted. A zero-fee transaction can only enter the mempool via package
evaluation, so this proves the CPFP package path end to end.

submitpackage is a bitcoind RPC, so the test skips on the btcd and
neutrino backends. Also adds the SubmitPackage wrapper to the
integration-test RPC harness.
Document the new WalletKit.SubmitPackage RPC and the lncli wallet
submitpackage command in the 0.22.0 release notes.
@ellemouton ellemouton force-pushed the walletkit-submitpackage branch from 4aae817 to 68c4740 Compare June 30, 2026 18:08

@yyforyongyu yyforyongyu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM⛵

@yyforyongyu yyforyongyu merged commit 40c64f9 into lightningnetwork:master Jun 30, 2026
41 of 42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gateway-active severity-critical Requires expert review - security/consensus critical

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants